{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Scaling to vector unit  length / unit norm\n",
    "\n",
    "Scaling to unit norm is achieved by dividing each feature vector by either the Manhattan distance (l1 norm) or the Euclidean distance of the vector (l2 norm):\n",
    "\n",
    "X_scaled_l1 = X / l1(X)\n",
    "\n",
    "X_scaled_l2 = X / l2(X)\n",
    "\n",
    "\n",
    "The **Manhattan distance** is given by the sum of the absolute components of the vector:\n",
    "\n",
    "l1(X) = |x1| + |x2| + ... + |xn|\n",
    "\n",
    "\n",
    "Whereas the **Euclidean distance** is given by the square root of the square sum of the component of the vector:\n",
    "\n",
    "l2(X) = sqr( x1^2 + x2^2 + ... + xn^2 )\n",
    "\n",
    "\n",
    "In the above example, x1 is variable 1, x2 variable 2, and xn variable n, and X is the data for 1 observation across variables (a row in other words).\n",
    "\n",
    "\n",
    "### Scaling to unit norm, examples\n",
    "\n",
    "For example, if our data has 1 observations (1 row) and 3 variables:\n",
    "\n",
    "- number of pets\n",
    "- number of children\n",
    "- age\n",
    "\n",
    "The values for each variable for that single observation are 10, 15 and 20. Our vector X = [10, 15, 20]. Then:\n",
    "\n",
    "l1(X) = 10 + 15 + 20 = 45\n",
    "\n",
    "l2(X) = sqr( 10^2 + 15^2 + 20^2) = sqr( 100 + 225 + 400) = **26.9**\n",
    "\n",
    "The euclidean distance is always smaller than the Manhattan distance.\n",
    "\n",
    "\n",
    "The normalized vector values are therefore:\n",
    "\n",
    "X_scaled_l1 = [ 10/45, 15/45, 20/45 ]      =  [0.22, 0.33, 0.44]\n",
    "\n",
    "X_scaled_l2 = [10/26.9, 15/26.9, 20/26.9 ] =  [0.37, 0.55, 0.74]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "from sklearn.datasets import fetch_california_housing\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "# the scaler - for robust scaling\n",
    "from sklearn.preprocessing import Normalizer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>MedInc</th>\n",
       "      <th>HouseAge</th>\n",
       "      <th>AveRooms</th>\n",
       "      <th>AveBedrms</th>\n",
       "      <th>Population</th>\n",
       "      <th>AveOccup</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>8.3252</td>\n",
       "      <td>41.0</td>\n",
       "      <td>6.984127</td>\n",
       "      <td>1.023810</td>\n",
       "      <td>322.0</td>\n",
       "      <td>2.555556</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>8.3014</td>\n",
       "      <td>21.0</td>\n",
       "      <td>6.238137</td>\n",
       "      <td>0.971880</td>\n",
       "      <td>2401.0</td>\n",
       "      <td>2.109842</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>7.2574</td>\n",
       "      <td>52.0</td>\n",
       "      <td>8.288136</td>\n",
       "      <td>1.073446</td>\n",
       "      <td>496.0</td>\n",
       "      <td>2.802260</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>5.6431</td>\n",
       "      <td>52.0</td>\n",
       "      <td>5.817352</td>\n",
       "      <td>1.073059</td>\n",
       "      <td>558.0</td>\n",
       "      <td>2.547945</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>3.8462</td>\n",
       "      <td>52.0</td>\n",
       "      <td>6.281853</td>\n",
       "      <td>1.081081</td>\n",
       "      <td>565.0</td>\n",
       "      <td>2.181467</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   MedInc  HouseAge  AveRooms  AveBedrms  Population  AveOccup\n",
       "0  8.3252      41.0  6.984127   1.023810       322.0  2.555556\n",
       "1  8.3014      21.0  6.238137   0.971880      2401.0  2.109842\n",
       "2  7.2574      52.0  8.288136   1.073446       496.0  2.802260\n",
       "3  5.6431      52.0  5.817352   1.073059       558.0  2.547945\n",
       "4  3.8462      52.0  6.281853   1.081081       565.0  2.181467"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# load the California House price data from Scikit-learn\n",
    "X, y = fetch_california_housing(return_X_y=True, as_frame=True)\n",
    "\n",
    "# Remove 2 variables:\n",
    "X.drop(labels=[\"Latitude\", \"Longitude\"], axis=1, inplace=True)\n",
    "\n",
    "# display top 5 rows\n",
    "X.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((14448, 6), (6192, 6))"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# let's separate the data into training and testing sets\n",
    "\n",
    "X_train, X_test, y_train, y_test = train_test_split(\n",
    "    X,\n",
    "    y,\n",
    "    test_size=0.3,\n",
    "    random_state=0,\n",
    ")\n",
    "\n",
    "X_train.shape, X_test.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Scaling to l1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# set up the scaler\n",
    "scaler = Normalizer(norm=\"l1\")  # for euclidean distance we change to norm='l2'\n",
    "\n",
    "# fit the scaler, this procedure does NOTHING\n",
    "scaler.fit(X_train)\n",
    "\n",
    "# transform train and test sets\n",
    "X_train_scaled = scaler.transform(X_train)\n",
    "X_test_scaled = scaler.transform(X_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 255.3,  889.1, 1421.7, ...,  744.6, 1099.5, 1048.9])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# let's calculate the norm for each observation (feature vector)\n",
    "# original data\n",
    "\n",
    "np.round(np.linalg.norm(X_train, ord=1, axis=1), 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1., 1., 1., ..., 1., 1., 1.])"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# let's calculate the norm for each observation (feature vector)\n",
    "# scaled data\n",
    "\n",
    "np.round(np.linalg.norm(X_train_scaled, ord=1, axis=1), 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Scaling to l2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# set up the scaler\n",
    "scaler = Normalizer(norm=\"l2\")\n",
    "\n",
    "# fit the scaler, this procedure does NOTHING\n",
    "scaler.fit(X_train)\n",
    "\n",
    "# transform train and test sets\n",
    "X_train_scaled = scaler.transform(X_train)\n",
    "X_test_scaled = scaler.transform(X_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 200. ,  837.1, 1387.1, ...,  704.7, 1052.6, 1024.1])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# let's calculate the norm for each observation (feature vector)\n",
    "# original data\n",
    "\n",
    "np.round(np.linalg.norm(X_train, ord=2, axis=1), 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1., 1., 1., ..., 1., 1., 1.])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# let's calculate the norm for each observation (feature vector)\n",
    "# scaled data\n",
    "\n",
    "np.round(np.linalg.norm(X_train_scaled, ord=2, axis=1), 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "fsml",
   "language": "python",
   "name": "fsml"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": "block",
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
